//7.1.4-1
/**
 * @param {string} str1
 * @remark 回调函数
 */
function f1(str1) {
    function doSomething(value, callback) {
        console.log('print>>>>>1',"Doing something...");
        console.log('print>>>>>2',value);
        return callback();
    }
      
    function doSomethingElse() {
        console.log('print>>>>>3',"Doing something else");
        return "Done";
    }
      
    return doSomething("Hello World", doSomethingElse);
}
// console.log(f1());
/*
print>>>>>1 Doing something...
print>>>>>2 Hello World
print>>>>>3 Doing something else
Done*/

//7.1.4-2
/**
 * @param {string} str2
 * @remark 事件监听 
 */
function f2(str2) {
    function f1() {
        setTimeout(function () {
          // ...
          f1.trigger('done');
        }, 1000);
    }
}
// console.log(f2());

//7.1.5-1
/**
 * @param {string} str3
 * @remark 编写一个流程控制函数，让它来控制异步任务，一个任务完成以后，再执行另一个。这就叫串行执行
 */
function f3(str3) {
    var items = [ 1, 2, 3, 4, 5, 6 ];
    var results = [];
    
    function async(arg, callback) {
      console.log('参数为 ' + arg +' , 1秒后返回结果');
      setTimeout(function () { callback(arg * 2); }, 1000);
    }
    
    function final(value) {
      console.log('完成: ', value);
    }
    
    function series(item) {
      if(item) {
        async( item, function(result) {
          results.push(result);
          return series(items.shift());
        });
      } else {
        return final(results[results.length - 1]);
      }
    }
    
    return series(items.shift());
}
// console.log(f3());
/*
参数为 1 , 1秒后返回结果
undefined
参数为 2, 1秒后返回结果
参数为 3 , 1秒后返回结果
参数为 4, 1秒后返回结果
参数为 5 , 1秒后返回结果
参数为 6 , 1秒后返回结果
完成:  12*/

// 7.1.5-2
/**
 * @param {string} str4
 * @remark 流程控制函数也可以是并行执行，即所有异步任务同时执行，等到全部完成以后，才执行final函数。
 */
function f4(str4) {
    var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];

function async(arg, callback) {
  console.log('参数为 ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function final(value) {
  console.log('完成: ', value);
}

items.forEach(function(item) {
  async(item, function(result){
    results.push(result);
    if(results.length === items.length) {
      final(results[results.length - 1]);
    }
  })
});
}
/*
console.log(f4());
参数为 1 , 1秒后返回结果
参数为 2, 1秒后返回结果
参数为 3 , 1秒后返回结果
参数为 4, 1秒后返回结果
参数为 5 , 1秒后返回结果
参数为 6 , 1秒后返回结果
undefined
完成:  12*/


//7.1.5-3
/**
 * @param {string} str5
 * @remark 所谓并行与串行的结合，就是设置一个门槛，每次最多只能并行执行n个异步任务，这样就避免了过分占用系统资源。 
 */
function f5(str5) {
    var items = [ 1, 2, 3, 4, 5, 6 ];
    var results = [];
    var running = 0;
    var limit = 2;
    
    function async(arg, callback) {
      console.log('参数为 ' + arg +' , 1秒后返回结果');
      setTimeout(function () { callback(arg * 2); }, 1000);
    }
    
    function final(value) {
      console.log('完成: ', value);
    }
    
    function launcher() {
      while(running < limit && items.length > 0) {
        var item = items.shift();
        async(item, function(result) {
          results.push(result);
          running--;
          if(items.length > 0) {
            launcher();
          } else if(running === 0) {
            final(results);
          }
        });
        running++;
      }
    }
    
    launcher();    
}
// console.log(f5());
/*
参数为 1 , 1秒后返回结果
参数为 2 , 1秒后返回结果
undefined
参数为 3 , 1秒后返回结果
参数为 4 , 1秒后返回结果
参数为 5 , 1秒后返回结果
参数为 6 , 1秒后返回结果
完成:  (6) [2, 4, 6, 8, 10, 12]
*/

//7.2.1
/**
 * @param {string} str6
 * @remark setTimeout函数接受两个参数，第一个参数func|code是将要推迟执行的函数名或者一段代码，第二个参数delay是推迟执行的毫秒数。
 */
function f6(str6) {
  console.log(1);
  setTimeout(function () { console.log(2) }, 1000)
  console.log(3);
} 

/**
 * @param {string} str7
 * @remark setTimeout还允许更多的参数。它们将依次传入推迟执行的函数（回调函数）。
 * @remark 最后那两个参数，将在1000毫秒之后回调函数执行时，作为回调函数的参数。
 */
function f7(str7) {
  setTimeout(function (a,b) {
    console.log(a + b);
  }, 1000, 1, 1);
}

/**
 * @param {string} str8
 * @remark 如果回调函数是对象的方法，那么setTimeout使得方法内部的this关键字指向全局环境，而不是定义时所在的那个对象。
 * @remark 因为当obj.y在1000毫秒后运行时，this所指向的已经不是obj了，而是全局环境。
 * @remark 一种解决方法是将obj.y放入一个函数。
 */
function f8(str8) {
  var x = 1;

  var obj = {
    x: 2,
    y: function () {
      console.log(this.x);
    }
  };
  
  setTimeout(function () {
    obj.y();
  }, 1000);
}

/**
 * @param {string} str9
 * @remark 使用bind方法，将obj.y这个方法绑定在obj上面。
 */
function f9(str9) {
  var x = 1;

  var obj = {
    x: 2,
    y: function () {
      console.log(this.x);
    }
  };
  
  setTimeout(obj.y.bind(obj), 1000);
}
// console.log(f6())
/*
1
3
undefined//前三行同一时间出现
2//延迟一秒
*/
// console.log(f7())
/*
undefined
2*/
// console.log(f8())
/*
undefined
2*/
// console.log(f9())
/*
undefined
2*/

//7.2.2
/**
 * @param {string} str10
 * @remark setInterval函数的用法与setTimeout完全一致，区别仅仅在于setInterval指定某个任务每隔一段时间就执行一次，也就是无限次的定时执行。
 */
function f10(str10) {
  var x = 1;

  var obj = {
    x: 2,
    y: function () {
      console.log(this.x);
    }
  };
  
  setInterval(obj.y.bind(obj), 1000);
}

//7.2.3
/**
 * @param {string} str11
 * @remark setTimeout和setInterval函数，都返回一个整数值，表示计数器编号。将该整数传入clearTimeout和clearInterval函数，就可以取消对应的定时器。
 */
function f11(str11) {
  var id1 = setTimeout(f, 1000);
  var id2 = setInterval(f, 1000);
  
  clearTimeout(id1);
  clearInterval(id2);
}

//7.2.4
/**
 * @param {string} str12
 * @remark 这种做法叫做 debounce（防抖动）。假定两次 Ajax 通信的间隔不得小于2500毫秒，上面的代码可以改写成下面这样。
 */
function f12(str12) {
  $('textarea').on('keydown', debounce(ajaxAction, 2500));

  function debounce(fn, delay){
    var timer = null; // 声明计时器
    return function() {
      var context = this;
      var args = arguments;
      clearTimeout(timer);
      timer = setTimeout(function () {
        fn.apply(context, args);
      }, delay);
    };
  }
}

//7.2.5
/**
 * @param {string} str13
 * @remark  运行机制:setTimeout和setInterval指定的回调函数，必须等到本轮事件循环的所有同步任务都执行完，才会开始执行。由于前面的任务到底需要多少时间执行完，是不确定的，所以没有办法保证，setTimeout和setInterval指定的任务，一定会按照预定时间执行。
 */
function f13(str13) {
  var x = 1;

  var obj = {
    x: 2,
    y: function () {
      console.log(this.x);
    }
  };
  
  var id = setInterval(obj.y.bind(obj), 1000);
  setTimeout(function () {
    clearInterval(id);
  }, 2000);
}
// console.log(f13())
/*
undefined
2*/

//7.2.6-1
/**
 * @param {string} str14
 * @remark setTimeout(f, 0)会在下一轮事件循环一开始就执行。
 */
function f14(str14) {
  setTimeout(function () {
    console.log(1);
  }, 0);
  console.log(2);
}
// console.log(f14())
/*
2
undefined
1*/

//7.3.1
/**
 * @param {string} str15
 * @remark Promise 的设计思想是，所有异步任务都返回一个 Promise 实例。Promise 实例有一个then方法，用来指定下一步的回调函数。
 * @remark f1的操作执行完成，就会执行f2。
 */
function f15(str15) {
  var p1 = new Promise(f1);
  p1.then(f2);
}

//7.3.4
/**
 * @param {string} str16
 * @remark Promise 实例的then方法，用来添加回调函数。
 */
function f16(str16) {
  var p1 = new Promise(function (resolve, reject) {
    resolve('成功');
  });
  p1.then(console.log, console.error);
  // "成功"
  
  var p2 = new Promise(function (resolve, reject) {
    reject(new Error('失败'));
  });
  p2.then(console.log, console.error);
  // Error: 失败
}

//7.3.8
/**
 * @param {string} str17
 * @remark then的回调函数属于异步任务，一定晚于同步任务执行。
 * @remark console.log(2)是同步任务
 */
function f17(str17) {
  new Promise(function (resolve, reject) {
    resolve(1);
  }).then(console.log);
  
  console.log(2);
}
/**
 * @param {string} str18
 * @remark Promise 的回调函数属于异步任务，会在同步任务之后执行。
 * @remark 但是，Promise 的回调函数不是正常的异步任务，而是微任务（microtask）。它们的区别在于，正常任务追加到下一轮事件循环，微任务追加到本轮事件循环。这意味着，微任务的执行时间一定早于正常任务。
 */
function f18(str18) {
  setTimeout(function() {
    console.log(1);
  }, 0);
  
  new Promise(function (resolve, reject) {
    resolve(2);
  }).then(console.log);
  
  console.log(3);
}
// console.log(f17())
/*
2
undefined
1*/
// console.log(f18())
/*
3
undefined
2
1*/